﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;

namespace CloudStorageLight.Core.Recommender
{
    /// <summary>
    /// リコメンドサービス
    /// </summary>
    public class RecommenderService
    {
        private HttpApplicationState httpApplication;

        protected virtual Core.BlobService CreateBlobService(CloudAccount account)
        {
            return new BlobService(account, httpApplication);
        }

        protected virtual Core.LogService CreateLogService()
        {
            return new Core.LogService(SystemSettings.Instance.BlobStorage);
        }

        public RecommenderService()
        {

        }

        public RecommenderService(HttpApplicationState app)
        {
            httpApplication = app;
        }
        

        /// <summary>
        /// リコメンド情報を作成します
        /// </summary>
        /// <returns></returns>
        public async virtual Task RecommenderAsync<T>(CloudAccount account, List<T> users, int targetDays=3, Func<BlobUser,BlobService,string,bool> recommending=null) where T : BlobUser
        {
            if (users == null || users.Count == 0) return;
            var logSvc = CreateLogService();

            var today = DateTime.UtcNow.Date;

            var accesslog = await logSvc.GetLog(account.AccountName, today.AddDays(-targetDays), today.AddDays(1), null);
            var updateFiles = accesslog.Where(x => x.AccountName == account.AccountName && x.Action == BlobService.LogUpload && users.Any(y => y.Name == x.UserName))
                .OrderByDescending(x => x.Date)
                .Select(x => x.Path)
                .Distinct();

            var recommendFiles = new List<string>();
            var list = new List<RecommendItem>();
            var idx = 0;

            foreach (var path in updateFiles.Distinct().OrderBy(x => x))
            {
                using(var svc = CreateBlobService(account))
                {
                    if (!await svc.ExistsAsync(path)) continue;
                    recommendFiles.Add(path);

                    var item = new RecommendItem();
                    item.Id = idx++;
                    item.Keywords = new Dictionary<string, int>();
                    list.Add(item);
                    var content = svc.GetFullText(path);
                    if (content != null) item.Keywords = GetKeywords(content);
                }
            }

            var engine = new RecommendEngine(list);

            foreach (var user in users)
            {
                var info = UserInfo.Get(SystemSettings.Instance.BlobStorage, account.AccountName, user.Name);
                info = info ?? new UserInfo(user.Name);
                var reco = (IRecommendedUser)info;
                var userAccess = accesslog.Where(x => x.UserName == user.Name).Select(x => x.Path).Distinct();

                var keywords = reco.PreferKeywords ?? new Dictionary<string, float>();

                foreach (var path in userAccess)
                {
                    var index = recommendFiles.IndexOf(path);
                    if (index >= 0)
                    {
                        foreach (var keyword in list[index].Keywords)
                        {
                            if (keywords.ContainsKey(keyword.Key))
                            {
                                keywords[keyword.Key] += keyword.Value;
                            }
                            else
                            {
                                keywords[keyword.Key] = keyword.Value;
                            }
                        }

                    }
                }

                var recommend = engine.GetTopRecommend(keywords);
                var finalRecommend = new List<string>();
                foreach (var path in recommend.Select(x => recommendFiles[x.Key.Id]))
                {
                    if (userAccess.Contains(path)) continue;
                    using (var svc = CreateBlobService(account))
                    {
                        if (!await svc.CanReadAsync(user, path)) continue;
                        if (recommending != null && !recommending(user, svc, path)) continue;
                        finalRecommend.Add(path);
                    }
                }
                reco.RecommendedList = finalRecommend;
                reco.PreferKeywords = keywords;
                LogRecommend(user.Name, account.AccountName, "", reco.RecommendedList.FirstOrDefault() ?? "");

            }

        }

        private void LogRecommend(string user, string account, string id, string message)
        {
            try
            {
                var svc = new LogService(SystemSettings.Instance.BlobStorage);
                svc.EntryLog(user, account, "recommend", id, message, httpApplication);
            }
            catch
            {
            }
        }

        private string keywordPattern = "[^ァ-ヶa-zA-Z一-龠-ー　]+";
        private Dictionary<string, int> GetKeywords(string content, int maxCount = 100)
        {
            var keywords = new Dictionary<string, int>();

            foreach (var item in Regex.Split(content, keywordPattern)
                .Select(x => x.Trim())
                .Where(x => x.Length >= 3)
                .GroupBy(x => x, x => x, (x, y) => new Tuple<string, int>(x, y.Count()), StringComparer.CurrentCultureIgnoreCase)
                .OrderByDescending(x => x.Item2)
                .Take(maxCount))
            {
                keywords.Add(item.Item1, item.Item2);
            }
            return keywords;
        }

    }
}
